package org.michael.u.brg.i18n.litz;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import javax.swing.*;
import java.awt.*;
import java.net.URI;
import java.util.HashSet;
import java.util.Set;

class KeyMapSaxHandler extends DefaultHandler {

    private char lastChar;
    private KeyStroke lastKeyStroke;
    private String lastCharsRead;
    private Font font;
    private String name;
    private Set<KeyPunch> keyPunches;
    private final URI fileUri;

    public KeyMapSaxHandler(URI fileUri) {
        this.fileUri = fileUri;
        keyPunches = new HashSet<KeyPunch>(1000);
    }

    public void characters(char chars[], int start, int length)
            throws SAXException {
        StringBuilder sb = new StringBuilder(length);
        int lastIdex = (length + start) - 1;
        for (int charIndex = start; charIndex <= lastIdex; charIndex++)
            sb.append(chars[charIndex]);

        lastCharsRead = sb.toString();
        super.characters(chars, start, length);
    }

    public void fatalError(SAXParseException e)
            throws SAXException {
        super.fatalError(e);
    }

    public void error(SAXParseException e)
            throws SAXException {
        throw e;
    }

    public void warning(SAXParseException e)
            throws SAXException {
        throw e;
    }

    public void startElement(String uri, String localName, String qName, Attributes attributes)
            throws SAXException {
        if ("keymap".equals(qName))
            handleMapTagStart(attributes);
        else if ("keydef".equals(qName))
            handleKeyTagStart(attributes);
    }

    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        if ("keymap".equals(qName))
            handleMapTagEnd();
        else if ("keydef".equals(qName))
            handleKeyTagEnd();
    }

    private void handleMapTagStart(Attributes attributes) {
        initMap();
        int attrbCount = attributes.getLength();
        for (int attrbIndex = 0; attrbIndex < attrbCount; attrbIndex++) {
            if ("name".equals(attributes.getQName(attrbIndex))) {
                name = attributes.getValue(attrbIndex);
                continue;
            }
            if ("font".equals(attributes.getQName(attrbIndex))) {
                String fontSpecs = attributes.getValue(attrbIndex);
                font = Font.decode(fontSpecs);
            }
        }
    }

    private void handleKeyTagStart(Attributes attributes) {
        initKey();
        int attrbCount = attributes.getLength();
        for (int attrbIndex = 0; attrbIndex < attrbCount; attrbIndex++) {
            if ("key".equals(attributes.getQName(attrbIndex))) {
                String keySpec = attributes.getValue(attrbIndex);
                lastKeyStroke = KeyStroke.getKeyStroke(keySpec);
                if (lastKeyStroke == null)
                    throw new RuntimeException(String.format("The key specification \"%s\" is invalid!", keySpec));
                continue;
            }
            if (!"char".equals(attributes.getQName(attrbIndex)))
                continue;
            String charSpec = attributes.getValue(attrbIndex);
            if (charSpec == null || charSpec.length() == 0)
                throw new RuntimeException(String.format("The char specification %s is invalid", charSpec));
            lastChar = charSpec.charAt(0);
        }

    }

    private void initMap() {
        name = null;
        font = null;
        initKey();
    }

    private void initKey() {
        lastKeyStroke = null;
        lastCharsRead = null;
        lastChar = '\0';
    }

    private void assertKey() {
        if (lastKeyStroke == null || lastCharsRead == null || lastCharsRead.length() < 1 || lastChar == 0)
            throw new RuntimeException("Error parsing " + fileUri + ": last keystroke = " + lastKeyStroke
            + ", last chars = \"" + lastCharsRead + "\", char=" + Integer.toString(lastChar));
    }

    private void assertMap() {
        if (name == null || font == null)
            throw new RuntimeException("No name or font parsing " + fileUri + ": name = " + name + ", font=" + font
                    + ", last key=" + lastKeyStroke + ", last chars = \"" + lastCharsRead + '"');
        if (keyPunches.size() < 1)
            throw new RuntimeException("Parsing " + fileUri + ": no keys defined");
    }

    private void handleMapTagEnd() {
        assertMap();
    }

    private void handleKeyTagEnd() {
        assertKey();
        keyPunches.add(new KeyPunch(lastKeyStroke, lastCharsRead, lastChar));
    }

    public void endDocument() throws SAXException {}

    public void startDocument()
            throws SAXException {
        name = null;
        font = null;
        keyPunches = new HashSet<KeyPunch>();
    }

    public Font getFont() {
        return font;
    }

    public String getName() {
        return name;
    }

    public Set<KeyPunch> getKeyPunches() {
        return keyPunches;
    }
}
